package com.environment.component;

import com.environment.Environment;
import com.environment.component.enumn.EWeather;
import com.environment.exception.MissingComponentException;
import com.environment.unit.Component;
import com.environment.unit.IComponent;
import com.jme.image.Texture;
import com.jme.math.FastMath;
import com.jme.math.Ring;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Controller;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jmex.effects.particles.ParticleFactory;
import com.jmex.effects.particles.ParticleMesh;
import com.jmex.effects.particles.SimpleParticleInfluenceFactory;
import com.jmex.effects.particles.ParticleSystem.ParticleType;

/**
 * <code>WeatherComponent</code> extends <code>Component</code> and maintains all the
 * weather effects.
 * <p>
 * The weather effects include rain and snow effects.
 * <p>
 * Rain and snow <code>Texture</code> have to be set prior to connecting to
 * <code>Environment</code>.
 * <p>
 * <code>WeatherComponent</code> does not require any connections with other
 * <code>Component</code> before initialization.
 * 
 * @author Yi Wang (Neakor)
 * @version Creation date: 04-10-2008 22:20 EST
 * @version Modified date: 06-15-2008 20:23 EST
 */
public class WeatherComponent extends Component {
	/**
	 * Serial version.
	 */
	private static final long serialVersionUID = 2552001957646997200L;
	/**
	 * The reference to <code>Environment</code> instance.
	 */
	private Environment environment;
	/**
	 * The radius of the weather region.
	 */
	private final float radius;
	/**
	 * The <code>ZBufferState</code> for particles.
	 */
	private ZBufferState zState;
	/**
	 * The rain particle <code>Texture</code>.
	 */
	private Texture rainTexture;
	/**
	 * The snow particle <code>Texture</code>.
	 */
	private Texture snowTexture;
	/**
	 * The rain effect <code>ParticleMesh</code>.
	 */
	private ParticleMesh rainParticles;
	/**
	 * The snow effect <code>ParticleMesh</code>.
	 */
	private ParticleMesh snowParticles;
	/**
	 * The temporary location.
	 */
	private final Vector3f tempLocation;

	/**
	 * Constructor of <code>WeatherComponent</code>.
	 */
	public WeatherComponent() {
		super(WeatherComponent.class.toString());
		this.radius = 100;
		this.tempLocation = new Vector3f();
	}

	@Override
	public boolean validate() throws MissingComponentException{
		return true;
	}

	@Override
	protected void initializeDetail() {
		this.buildZBufferState();
		Ring ring = new Ring(new Vector3f(0, 0, 0), Vector3f.UNIT_Y, 0, this.radius);
		this.buildRainEffect(ring);
		this.buildSnowEffect(ring);
	}
	
	/**
	 * Build the <code>ZBufferState</code> for the particles.
	 */
	private void buildZBufferState() {
		this.zState = this.environment.getRenderer().createZBufferState();
		this.zState.setEnabled(true);
		this.zState.setWritable(false);
	}
	
	/**
	 * Build the rain effect.
	 * @param ring The particles emitting plane.
	 */
	private void buildRainEffect(Ring ring) {
		// Build the rain particles.
		this.rainParticles = ParticleFactory.buildParticles("RainParticles", 1200, ParticleType.Quad);
		this.rainParticles.setGeometry(ring);
		TextureState texState = this.environment.getRenderer().createTextureState();
		texState.setTexture(this.rainTexture);
		this.rainParticles.setRenderState(texState);
		this.rainParticles.setRenderState(this.environment.getAlphaState());
		this.rainParticles.setRenderState(this.zState);
		this.rainParticles.updateRenderState();
		// Initialize the effect.
		this.rainParticles.addInfluence(SimpleParticleInfluenceFactory.createBasicGravity(new Vector3f(0, -3f, 0), false));
		this.rainParticles.addInfluence(SimpleParticleInfluenceFactory.createBasicWind(5, new Vector3f(0.2f,0,-0.2f), true, true));
		this.rainParticles.setEmissionDirection(new Vector3f(0.0f, -1.0f, 0.0f));
		this.rainParticles.setMaximumAngle(0);
		this.rainParticles.setMinimumAngle(0);
		this.rainParticles.getParticleController().setSpeed(0.25f);
		this.rainParticles.setMinimumLifeTime(1000.0f);
		this.rainParticles.setMaximumLifeTime(2000.0f);
		this.rainParticles.setStartSize(0.5f);
		this.rainParticles.setEndSize(0.5f);
		this.rainParticles.setStartColor(ColorRGBA.white);
		this.rainParticles.setEndColor(ColorRGBA.gray);
		this.rainParticles.setReleaseRate(1200);
		this.rainParticles.setReleaseVariance(0.0f);
		this.rainParticles.setInitialVelocity(0.3f);
		this.rainParticles.setRotateWithScene(true);
		this.rainParticles.getParticleController().setControlFlow(true);
		this.rainParticles.getParticleController().setRepeatType(Controller.RT_WRAP);
		this.rainParticles.getParticleController().setActive(true);
		this.rainParticles.forceRespawn();
	}
	
	/**
	 * Build the snow effect.
	 * @param ring The particles emitting plane.
	 */
	private void buildSnowEffect(Ring ring) {
		// Build the snow effect.
		this.snowParticles = ParticleFactory.buildParticles("SnowParticles", 1200, ParticleType.Quad);
		this.snowParticles.setGeometry(ring);
		TextureState texState = this.environment.getRenderer().createTextureState();
		texState.setTexture(this.snowTexture);
		this.snowParticles.setRenderState(texState);
		this.snowParticles.setRenderState(this.environment.getAlphaState());
		this.snowParticles.setRenderState(this.zState);
		this.snowParticles.updateRenderState();
		// Initialize the effect.
		this.snowParticles.addInfluence(SimpleParticleInfluenceFactory.createBasicGravity(new Vector3f(0, -2f, 0), false));
		this.snowParticles.addInfluence(SimpleParticleInfluenceFactory.createBasicDrag(25));
		this.snowParticles.addInfluence(SimpleParticleInfluenceFactory.createBasicWind(5, new Vector3f(0.2f,0,-0.2f), true, true));
		this.snowParticles.setEmissionDirection(new Vector3f(0.0f, -1.0f, 0.0f));
		this.snowParticles.setMaximumAngle(FastMath.HALF_PI);
		this.snowParticles.setMinimumAngle(0);
		this.snowParticles.getParticleController().setSpeed(0.1f);
		this.snowParticles.setMinimumLifeTime(2000.0f);
		this.snowParticles.setMaximumLifeTime(3000.0f);
		this.snowParticles.setStartSize(0.5f);
		this.snowParticles.setEndSize(0.5f);
		this.snowParticles.setStartColor(new ColorRGBA(1, 1, 1, 1.0f));
		this.snowParticles.setEndColor(new ColorRGBA(1, 1, 1, 0.2f));
		this.snowParticles.setReleaseRate(1200);
		this.snowParticles.setReleaseVariance(0.0f);
		this.snowParticles.setInitialVelocity(0.2f);
		this.snowParticles.setRotateWithScene(true);
		this.snowParticles.getParticleController().setControlFlow(true);
		this.snowParticles.getParticleController().setRepeatType(Controller.RT_WRAP);
		this.snowParticles.getParticleController().setActive(true);
		this.snowParticles.forceRespawn();
	}

	@Override
	protected void updateDetail(float time) {
		this.tempLocation.set(this.environment.getRenderer().getCamera().getLocation());
		this.snowParticles.setLocalTranslation(this.tempLocation.x, this.tempLocation.y+0.5f*this.radius, this.tempLocation.z);
		this.rainParticles.setLocalTranslation(this.tempLocation.x, this.tempLocation.y+0.5f*this.radius, this.tempLocation.z);
	}

	@Override
	public void connect(IComponent component) {
		if(component instanceof Environment) {
			this.environment = (Environment)component;
		}
	}

	/**
	 * Set the rain particle <code>Texture</code>.
	 * @param texture The rain particle <code>Texture</code>.
	 */
	public void setRainTexture(Texture texture) {
		this.rainTexture = texture;
	}
	
	/**
	 * Set the snow particle <code>Texture</code>.
	 * @param texture The snow particle <code>Texture</code>.
	 */
	public void setSnowTexture(Texture texture) {
		this.snowTexture = texture;
	}
	
	/**
	 * Retrieve the <code>ParticleMesh</code> with given <code>Weather</code> type.
	 * @param weather The <code>Weather</code> type to retrieve.
	 * @return The <code>ParticleMesh</code> with given <code>Weather</code> type.
	 */
	public ParticleMesh getWeather(EWeather weather) {
		switch(weather) {
		case Rain:
			return this.rainParticles;
		case Snow:
			return this.snowParticles;
		}
		return null;
	}
}
